#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
3761协议报文解析器 - 正则表达式版本
使用正则表达式快速提取和解析TAG数据
"""

import re
import struct
from typing import Dict, List, Tuple, Optional


class Q3761RegexParser:
    """使用正则表达式的3761协议解析器"""
    
    # TAG定义：TAG_ID -> (名称, 字节长度, 解析函数名)
    TAG_DEFINITIONS = {
        0x0001: ('表号', 6, 'parse_meter_id'),
        0x0002: ('时间', 7, 'parse_datetime'),
        0x0006: ('上1次日冻结时间', 5, 'parse_freeze_time'),
        0x0007: ('上1次日冻结正向有功电能数据', 20, 'parse_raw_hex'),
        0x0008: ('上1次日冻结反向有功电能数据', 20, 'parse_raw_hex'),
        0x0014: ('电表运行状态字3', 2, 'parse_status_word'),
        0x0015: ('A相电压', 2, 'parse_voltage'),
        0x0016: ('B相电压', 2, 'parse_voltage'),
        0x0017: ('C相电压', 2, 'parse_voltage'),
        0x0018: ('A相电流', 3, 'parse_current'),
        0x0019: ('B相电流', 3, 'parse_current'),
        0x001A: ('C相电流', 3, 'parse_current'),
        0x001B: ('瞬时总有功功率', 3, 'parse_power'),
        0x001C: ('瞬时A相有功功率', 3, 'parse_power'),
        0x001D: ('瞬时B相有功功率', 3, 'parse_power'),
        0x001E: ('瞬时C相有功功率', 3, 'parse_power'),
        0x001F: ('瞬时总视在功率', 3, 'parse_apparent_power'),
        0x0020: ('瞬时A相视在功率', 3, 'parse_apparent_power'),
        0x0021: ('瞬时B相视在功率', 3, 'parse_apparent_power'),
        0x0022: ('瞬时C相视在功率', 3, 'parse_apparent_power'),
        0x0023: ('总功率因数', 2, 'parse_power_factor'),
        0x0024: ('A相功率因数', 2, 'parse_power_factor'),
        0x0025: ('B相功率因数', 2, 'parse_power_factor'),
        0x0026: ('C相功率因数', 2, 'parse_power_factor'),
        0x0027: ('（当前）组合有功总电能', 4, 'parse_energy'),
        0x0028: ('（当前）组合有功费率1电能', 4, 'parse_energy'),
        0x0029: ('（当前）组合有功费率2电能', 4, 'parse_energy'),
        0x002A: ('（当前）组合有功费率3电能', 4, 'parse_energy'),
        0x002B: ('（当前）组合有功费率4电能', 4, 'parse_energy'),
        0x002C: ('（当前）正向有功总电能', 4, 'parse_energy'),
        0x002D: ('（当前）正向有功费率1电能', 4, 'parse_energy'),
        0x002E: ('（当前）正向有功费率2电能', 4, 'parse_energy'),
        0x002F: ('（当前）正向有功费率3电能', 4, 'parse_energy'),
        0x0030: ('（当前）正向有功费率4电能', 4, 'parse_energy'),
        0x0049: ('（当前）组合无功1总电能', 4, 'parse_energy'),
        0x004A: ('（当前）组合无功1费率1电能', 4, 'parse_energy'),
        0x004B: ('（当前）组合无功1费率2电能', 4, 'parse_energy'),
        0x004C: ('（当前）组合无功1费率3电能', 4, 'parse_energy'),
        0x004D: ('（当前）组合无功1费率4电能', 4, 'parse_energy'),
        0x004E: ('（当前）组合无功2总电能', 4, 'parse_energy'),
        0x004F: ('（当前）组合无功2费率1电能', 4, 'parse_energy'),
        0x0050: ('（当前）组合无功2费率2电能', 4, 'parse_energy'),
        0x0051: ('（当前）组合无功2费率3电能', 4, 'parse_energy'),
        0x0052: ('（当前）组合无功2费率4电能', 4, 'parse_energy'),
    }
    
    def __init__(self):
        self.parsed_tags = []
    
    @staticmethod
    def parse_bcd(data: bytes) -> str:
        """解析BCD码"""
        bcd_str = ""
        for byte in data:
            bcd_str += f"{(byte>>4)&0x0F}{byte&0x0F}"
        return bcd_str
    
    # ========== TAG数据解析函数 ==========
    
    def parse_meter_id(self, data: bytes) -> str:
        """解析表号"""
        return ''.join([f"{b:02X}" for b in data])
    
    def parse_datetime(self, data: bytes) -> str:
        """解析时间"""
        if len(data) >= 7:
            return f"20{data[0]:02X}-{data[1]:02X}-{data[2]:02X} {data[4]:02X}:{data[5]:02X}:{data[6]:02X}"
        elif len(data) >= 5:
            return f"20{data[0]:02X}-{data[1]:02X}-{data[2]:02X}"
        return self.parse_raw_hex(data)
    
    def parse_freeze_time(self, data: bytes) -> str:
        """解析冻结时间"""
        if len(data) >= 5:
            return f"{data[0]:02X}{data[1]:02X}{data[2]:02X}{data[3]:02X}{data[4]:02X}"
        return self.parse_raw_hex(data)
    
    def parse_status_word(self, data: bytes) -> str:
        """解析状态字"""
        if len(data) >= 2:
            value = struct.unpack('>H', data[:2])[0]
            return f"0x{value:04X}"
        return self.parse_raw_hex(data)
    
    def parse_voltage(self, data: bytes) -> str:
        """解析电压 (2字节BCD, 单位0.1V)"""
        if len(data) >= 2:
            bcd_str = self.parse_bcd(data[:2])
            value = int(bcd_str) / 10
            return f"{value:.1f}V"
        return self.parse_raw_hex(data)
    
    def parse_current(self, data: bytes) -> str:
        """解析电流 (3字节BCD, 单位0.001A)"""
        if len(data) >= 3:
            bcd_str = self.parse_bcd(data[:3])
            value = int(bcd_str) / 1000
            return f"{value:.3f}A"
        return self.parse_raw_hex(data)
    
    def parse_power(self, data: bytes) -> str:
        """解析功率 (3字节BCD, 单位0.001kW)"""
        if len(data) >= 3:
            bcd_str = self.parse_bcd(data[:3])
            value = int(bcd_str) / 1000
            return f"{value:.3f}kW"
        return self.parse_raw_hex(data)
    
    def parse_apparent_power(self, data: bytes) -> str:
        """解析视在功率 (3字节BCD, 单位0.001kVA)"""
        if len(data) >= 3:
            bcd_str = self.parse_bcd(data[:3])
            value = int(bcd_str) / 1000
            return f"{value:.3f}kVA"
        return self.parse_raw_hex(data)
    
    def parse_power_factor(self, data: bytes) -> str:
        """解析功率因数 (2字节BCD, 单位0.001)"""
        if len(data) >= 2:
            bcd_str = self.parse_bcd(data[:2])
            value = int(bcd_str) / 1000
            return f"{value:.3f}"
        return self.parse_raw_hex(data)
    
    def parse_energy(self, data: bytes) -> str:
        """解析电能 (4字节BCD, 固定2位小数)"""
        if len(data) >= 4:
            bcd_str = self.parse_bcd(data[:4])
            trimmed_bcd = bcd_str.lstrip('0') or '0'
            
            if len(trimmed_bcd) >= 2:
                integer_part = trimmed_bcd[:-2] if len(trimmed_bcd) > 2 else '0'
                decimal_part = trimmed_bcd[-2:]
                if not integer_part:
                    integer_part = '0'
                return f"{integer_part}.{decimal_part}kWh"
            else:
                return f"0.{trimmed_bcd.zfill(2)}kWh"
        return self.parse_raw_hex(data)
    
    def parse_raw_hex(self, data: bytes) -> str:
        """原始十六进制显示"""
        return data.hex().upper()
    
    def extract_tags_sequential(self, data: bytes) -> List[Tuple[int, str, bytes]]:
        """
        顺序提取TAG数据（避免误匹配）
        
        返回: [(tag_id, tag_name, tag_data), ...]
        """
        results = []
        pos = 0
        
        while pos + 2 <= len(data):
            # 读取TAG ID (大端序)
            tag_id = struct.unpack('>H', data[pos:pos+2])[0]
            
            # 检查是否是已知TAG
            if tag_id in self.TAG_DEFINITIONS:
                tag_name, data_len, _ = self.TAG_DEFINITIONS[tag_id]
                pos += 2
                
                # 检查是否有足够的数据
                if pos + data_len <= len(data):
                    tag_data = data[pos:pos+data_len]
                    results.append((tag_id, tag_name, tag_data))
                    pos += data_len
                else:
                    # 数据不足，结束解析
                    break
            else:
                # 未知TAG，跳过1字节继续搜索
                pos += 1
        
        return results
    
    def extract_tags_regex(self, data: bytes) -> List[Tuple[int, str, bytes]]:
        """
        使用正则表达式提取所有TAG数据（优化版：只匹配连续的TAG序列）
        
        返回: [(tag_id, tag_name, tag_data), ...]
        """
        # 先用顺序方法，因为TAG应该是连续排列的
        # 正则适合用于查找特定TAG，而不是全量提取
        return self.extract_tags_sequential(data)
    
    def find_tag_by_regex(self, data: bytes, tag_id: int) -> Optional[bytes]:
        """
        使用正则表达式快速查找特定TAG
        
        Args:
            data: 报文数据
            tag_id: TAG ID
        
        Returns:
            TAG数据，如果未找到则返回None
        
        示例:
            parser.find_tag_by_regex(data, 0x0027)  # 查找组合有功总电能
        """
        if tag_id not in self.TAG_DEFINITIONS:
            return None
        
        _, data_len, _ = self.TAG_DEFINITIONS[tag_id]
        
        # 构造正则模式
        tag_bytes = struct.pack('>H', tag_id)
        pattern = re.escape(tag_bytes) + b'(.{' + str(data_len).encode() + b'})'
        
        match = re.search(pattern, data)
        if match:
            return match.group(1)
        return None
    
    def find_multiple_tags_regex(self, data: bytes, tag_ids: List[int]) -> Dict[int, bytes]:
        """
        使用正则表达式批量查找多个TAG
        
        Args:
            data: 报文数据
            tag_ids: TAG ID列表
        
        Returns:
            {tag_id: tag_data} 字典
        
        示例:
            # 只关心电能数据
            energy_tags = [0x0027, 0x0049, 0x004E]
            results = parser.find_multiple_tags_regex(data, energy_tags)
        """
        results = {}
        
        for tag_id in tag_ids:
            tag_data = self.find_tag_by_regex(data, tag_id)
            if tag_data:
                results[tag_id] = tag_data
        
        return results
    
    def parse_message(self, message: str) -> Dict:
        """
        解析3761协议报文
        
        Args:
            message: 十六进制字符串报文，可以包含空格
        
        Returns:
            解析结果字典
        """
        # 清理报文字符串
        message = message.replace(' ', '').replace('\n', '')
        
        try:
            data = bytes.fromhex(message)
        except ValueError as e:
            return {'error': f'报文格式错误: {e}'}
        
        # 基本帧结构解析
        if len(data) < 16:
            return {'error': '报文长度不足'}
        
        result = {
            'frame_header': {
                'start': f"0x{data[0]:02X}",
                'length': struct.unpack('<H', data[1:3])[0] >> 2,
                'control': f"0x{data[6]:02X}",
            },
            'application': {
                'afn': f"0x{data[12]:02X}",
                'seq': f"0x{data[13]:02X}",
            },
            'tags': []
        }
        
        # 提取应用层数据
        app_data = data[12:-2]  # 去掉帧头和校验
        data_unit = app_data[6:]  # 跳过AFN+SEQ+数据单元标识
        
        # 查找TAG数据起始位置
        # 方法1: 查找TAG 0x0001的位置
        tag_start_pattern = b'\x00\x01'
        match = re.search(tag_start_pattern, data_unit)
        
        if match:
            tag_data = data_unit[match.start():]
            
            # 使用正则提取所有TAG
            extracted_tags = self.extract_tags_regex(tag_data)
            
            # 解析每个TAG的值
            for tag_id, tag_name, tag_data in extracted_tags:
                _, _, parse_func_name = self.TAG_DEFINITIONS[tag_id]
                parse_func = getattr(self, parse_func_name)
                parsed_value = parse_func(tag_data)
                
                result['tags'].append({
                    'tag_id': f"0x{tag_id:04X}",
                    'tag_name': tag_name,
                    'raw_data': tag_data.hex().upper(),
                    'parsed_value': parsed_value,
                    'is_energy': '电能' in tag_name
                })
        
        return result
    
    def format_result(self, result: Dict) -> str:
        """格式化输出解析结果"""
        if 'error' in result:
            return f"❌ {result['error']}"
        
        lines = []
        lines.append("=" * 70)
        lines.append("3761协议报文解析结果 (正则表达式版)")
        lines.append("=" * 70)
        
        # 帧头信息
        lines.append("\n【帧头信息】")
        frame = result['frame_header']
        lines.append(f"起始符: {frame['start']}")
        lines.append(f"长度域: {frame['length']} 字节")
        lines.append(f"控制域: {frame['control']}")
        
        # 应用层信息
        lines.append("\n【应用层信息】")
        app = result['application']
        lines.append(f"AFN: {app['afn']}")
        lines.append(f"SEQ: {app['seq']}")
        
        # TAG数据
        if result['tags']:
            lines.append("\n【电表数据】")
            for tag in result['tags']:
                marker = " ⭐" if tag['is_energy'] else ""
                lines.append(f"{tag['tag_name']}: {tag['parsed_value']}{marker}")
        else:
            lines.append("\n⚠️  未找到TAG数据")
        
        lines.append("\n" + "=" * 70)
        
        return '\n'.join(lines)


def main():
    """测试函数"""
    parser = Q3761RegexParser()
    
    # 测试报文1: 组合有功电能
    print("\n测试报文1: 组合有功电能数据")
    test_message1 = "68 42 01 42 01 68 CA 05 06 33 A0 01 10 75 00 00 20 1F 00 1A 00 00 00 01 00 00 FF 00 38 00 FD 00 34 00 00 00 00 07 00 01 69 35 06 05 00 51 00 02 25 09 30 02 09 58 00 00 27 00 03 67 50 00 28 00 03 67 50 00 29 00 00 00 00 00 2A 00 00 00 00 00 2B 00 00 00 00 37 2D 16"
    
    result1 = parser.parse_message(test_message1)
    print(parser.format_result(result1))
    
    # 测试报文2: 组合无功电能
    print("\n" + "=" * 70)
    print("\n测试报文2: 组合无功电能数据")
    test_message2 = "68 6A 02 6A 02 68 CA 05 06 33 A0 01 10 78 00 00 20 1F 00 1A 00 00 00 01 00 00 FF 00 82 00 FD 00 7E 00 00 00 00 10 00 01 69 35 06 05 00 51 00 02 25 09 27 06 00 36 01 00 06 25 09 27 00 00 00 07 00 03 59 84 00 03 59 84 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 15 23 52 00 18 00 01 85 00 1B 00 02 72 00 1C 00 02 72 00 1F 00 04 33 00 20 00 04 33 00 23 06 25 00 24 06 25 00 49 00 01 14 01 00 4E 00 00 00 00 96 38 16"
    
    result2 = parser.parse_message(test_message2)
    print(parser.format_result(result2))
    
    # 演示正则表达式快速查找特定TAG
    print("\n" + "=" * 70)
    print("\n✨ 正则表达式特色功能演示：快速查找特定TAG")
    print("=" * 70)
    
    data = bytes.fromhex(test_message1.replace(' ', ''))
    app_data = data[12:-2]
    tag_data = app_data[6:]
    
    # 查找TAG数据起始位置
    match = re.search(b'\x00\x01', tag_data)
    if match:
        tag_region = tag_data[match.start():]
        
        # 快速查找特定电能TAG
        print("\n【示例1】快速查找组合有功总电能 (TAG 0x0027):")
        energy_data = parser.find_tag_by_regex(tag_region, 0x0027)
        if energy_data:
            parsed = parser.parse_energy(energy_data)
            print(f"  找到数据: {energy_data.hex().upper()}")
            print(f"  解析值: {parsed}")
        
        # 批量查找多个电能TAG
        print("\n【示例2】批量查找多个电能TAG:")
        energy_tags = [0x0027, 0x0028, 0x0029, 0x002A, 0x002B]
        results = parser.find_multiple_tags_regex(tag_region, energy_tags)
        for tag_id, tag_data in results.items():
            tag_name = parser.TAG_DEFINITIONS[tag_id][0]
            parsed = parser.parse_energy(tag_data)
            print(f"  TAG 0x{tag_id:04X} ({tag_name}): {parsed}")
        
        # 查找表号和时间
        print("\n【示例3】快速提取表号和时间:")
        meter_id = parser.find_tag_by_regex(tag_region, 0x0001)
        datetime_data = parser.find_tag_by_regex(tag_region, 0x0002)
        if meter_id:
            print(f"  表号: {parser.parse_meter_id(meter_id)}")
        if datetime_data:
            print(f"  时间: {parser.parse_datetime(datetime_data)}")
    
    # 性能对比说明
    print("\n" + "=" * 70)
    print("✨ 正则表达式版本的优势：")
    print("  1. 快速定位：使用正则快速找到特定TAG，无需遍历")
    print("  2. 灵活查询：可以只提取关心的TAG，节省处理时间")
    print("  3. 代码简洁：TAG定义集中管理，易于维护和扩展")
    print("  4. 批量操作：一次查询可以获取多个指定TAG")
    print("  5. 模式匹配：可以轻松扩展支持复杂的匹配规则")
    print("\n使用场景：")
    print("  • 只关心特定数据（如电能）时，快速提取")
    print("  • 数据验证时，快速检查必要TAG是否存在")
    print("  • 大批量报文处理时，提高解析效率")
    print("=" * 70)


if __name__ == "__main__":
    main()
